/*
 * HtmlDataHandler.cpp
 *
 *  Created on: 2010-5-3
 *      Author: anod
 */
#include <unistd.h>

#include <typeinfo>
#include <memory>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <Module-AppData/HtmlDataParser.hpp>
#include <Module-AppData/HtmlDataHandler.hpp>
#include <Module-AppData/ParseEvent.hpp>

#include <Module-AppData/parser/ScriptParser.hpp>
#include <Module-AppData/parser/DllFuncParser.hpp>
#include <Module-AppData/parser/ProcessParser.hpp>


class HandlerPrivate
{
public:
	HandlerPrivate()
	:pin(-1), pout(-1), stdin(-1), stdout(-1)
	{
		strcpy(pin_name, "/tmp/parse_in-XXXXXX");
		strcpy(pout_name, "/tmp/parse_out-XXXXXX");
	}
	~HandlerPrivate()
	{
		dup2( stdin, 0 );
		close( pin );

		unlink( pin_name );
		unlink( pout_name );
	}

	// 打开一个临时文件，把原来的标准输出流变成标准输入流，临时文件变成标准输出流
	int setup_pipe_in()
	{
		int out = mkstemp( pin_name );
		if( out < 0 )
		  return 1;
		pin = out;

		out = dup(0);// 保存原来的标准输入流
		if( out < 0 )
		  return 2;
		stdin = out;

		std::cout.flush();
		out = dup2( pout, 0 );
		if( out < 0 )
		  return 3;
		lseek( 0, 0, SEEK_SET );

		out = dup2( pin, 1 );
		if( out < 0 )
		  return 4;
		else
		  return 0;
	}

	// 打开一个临时文件，重新定义标准输出流
	int setup_pipe_out()
	{
		int out = mkstemp( pout_name );
		if( out<0 )//打开出错
		  return 1;
		pout = out;

		out = dup(1);// 保存原来的标准输出文件描述符
		if( out < 0 )
		  return 2;
		stdout = out;

		out = dup2(pout, 1);//定义新的输出流
		if( out < 0 )
		  return 2;
		else
		  return 0;
	}

	// 把原来的输入输出流整回去
	int resume_env()
	{
		std::cout.flush();
		int out = dup2( pin, 0 );
		if( out < 0 )
		  return 1;
		lseek( 0, 0, SEEK_SET );
		std::cin.clear();

		out = dup2( stdout, 1 );
		if( out < 0 )
		  return 2;

		close( pout );
		return 0;
	}

	size_t get_out_length()
	{
		int here = std::cin.tellg();
		std::cin.seekg(0, std::ios::end);
		size_t ret = std::cin.tellg();
		std::cin.seekg(0);
		return ret;
	}

private:
	// 临时文件……，用于在parse函数被调用的时候进行通信
	char pin_name[sizeof("/tmp/parse_in-XXXXXX")];
	char pout_name[sizeof("/tmp/parse_out-XXXXXX")];
	int  pin, pout;

	// 原来的stdin/stdout的文件描述符
	int stdin, stdout;
};



namespace AppData
{

HtmlDataHandler::HtmlDataHandler(int policy)
:_impl(NULL)
{
	setPolicy(policy);
}

HtmlDataHandler::~HtmlDataHandler()
{
	if( _impl!= NULL )
	{
		delete _impl;
		_impl = NULL;
	}
}

int HtmlDataHandler::getPolicy()
{
	int ret;
	if( _impl==NULL )
	{
		ret = -1;
	}
	else if( typeid(*_impl)==typeid(ScriptParser) )
	{
		ret = AppConfig::WidgetConfig::POLICY_TYPE_SCRIPT;
	}
	else if( typeid(*_impl)==typeid(ProcessParser) )
	{
		ret = AppConfig::WidgetConfig::POLICY_TYPE_PROCESS;
	}
	else if( typeid(*_impl)==typeid(DllFuncParser) )
	{
		ret = AppConfig::WidgetConfig::POLICY_TYPE_SHAREDLIB;
	}

	return ret;
}

void HtmlDataHandler::setPolicy(int policy)
{
	// 把旧的impl清除
	HtmlDataParser::destroyParser( _impl );

	// 得到一个新的impl
	_impl = HtmlDataParser::createParser( policy );
}

void HtmlDataHandler::parseHtml(const char* html) throw(std::logic_error)
{
	if( _impl==NULL ) //还没有设置策略就想执行，直接抛异常
	{
		throw( std::logic_error("parse policy has not been set yet!") );
	}

	std::auto_ptr<HandlerPrivate> _p( new HandlerPrivate );

	// 建立管道连接输出文件
	_p->setup_pipe_out();

	// 把页面输出到管道文件中
	std::cout << html;

	// 建立管道连接输入文件
	_p->setup_pipe_in();

	int ret = _impl->parse();
	_p->resume_env();
	ParseEvent event(ParseEvent::DATA_DAMAGED);
	std::auto_ptr<char> _output_buffer;
	if( ret == 0 )//成功
	{
		size_t         _output_buflen = _p->get_out_length();
		_output_buffer = std::auto_ptr<char>( new char[_output_buflen+1] );

		//保存xml输出
		std::cin.read(_output_buffer.get(), _output_buflen);
		_output_buffer.get()[_output_buflen] = '\0';

		//填写事件
		event = ParseEvent(ParseEvent::DATA_READY, _output_buffer.get(), std::cin.gcount());
	}

	//发送事件
	eventHappen(event);
}

}
